Org-mode: Dynamic block

Un nouveau billet de blog pour te parler d'une fonctionnalité d'Org-mode très pratique: Les blocs de texte dynamique.

Comme son nom l'indique, il s'agit d'un bloc de texte, inséré dans un document Org-mode, dont le contenu sera généré dynamiquement par Emacs. Il existe plusieurs types de blocs, chacun générant un contenu différent. On peut paramétrer chaque bloc afin de personnaliser le contenu, chaque type de bloc acceptant des paramètres différents. Et il est aussi possible de créer de nouveaux types de blocs.

On va voir comment utiliser ces blocs mais aussi comment en créer un nouveau.

Avant de commencer

Avec les blocs dynamiques, il faut faire la différence entre:

  • Le bloc qu'on ajoute dans un document Org-mode, qui sera d'un certain type
  • Le contenu généré par le bloc
  • La façon d'ajouter le bloc dans un document

Utilisation

Ajouter un bloc dans un document

Pour ajouter un bloc dynamique à un document Org-mode, on peut procéder de 2 façons:

  • L'écrire sois-même
  • Demander à Emacs d'écrire automatiquement le bloc

Pour la première option, on écrit simplement ceci:

#+BEGIN type-de-bloc

#+END

Les balises #+BEGIN et #+END indiquent le début et la fin du bloc. Il faut remplacer type-de-bloc par le type de bloc qu'on souhaite. On écrit rien entre les balises car c'est là que le contenu généré dynamiquement sera écrit.

Pour demander à Emacs d'ajouter le bloc au lieu de l'écrire sois-même, on peut appeler la fonction org-dynamic-block-insert-dblock (raccourcis clavier C-c C-x x). Emacs demandera le nom du type de bloc qu'on souhaite utiliser et il insérera un exemple de ce bloc.

Demander au bloc de générer son contenu

Pour générer le contenue du bloc, il faut placer le curseur d'écriture sur la ligne #+BEGIN et appeler la fonction org-dblock-update (raccourcis clavier C-c C-c).

Tout ce qui se trouve entre le #+BEGIN et le #+END sera effacé et le texte généré y sera inséré.

Paramétrer le bloc

Il est possible de définir des paramètres pour le bloc, sur la même ligne que la balise #+BEGIN, sous cette forme:

#+BEGIN type-de-bloc :parametre_1 11 :parametre_2 42

#+END

Dans cette exemple, on a 2 paramètres: parametre_1 qui a pour valeur 11 et parametre_2 qui a pour valeur 42.

Types de blocs proposés par Org-mode

Org-mode vient avec 2 types de blocs dynamiques: clocktable et columnview. Le premier va insérer un tableau qui listera des tâches qu'on a fait ainsi que le temps qu'on y a passé. Le second affichera une arborescence d'un document Org-mode sous la forme d'un tableau.

Comme cette article est déjà assez long, je ne vais pas détailler l'utilisation de ces 2 blocs ici. Mais je t'invite à aller lire la documentation de clocktable et celle de columnview.

Créer un nouveau type de bloc

Pour ce nouveau type de bloc, je vais prendre un exemple simple: Un bloc qui va répéter une phrase.

Il sera nommé example, va accepter le paramètre :text et le contenu généré sera une copie du texte reçu comme paramètre.

Définition du nouveau type de bloc

Je commence par écrire une fonction Emacs-lisp comme celle-ci:

(defun org-dblock-write:example (params)
  (let ((text (plist-get params :text)))
    (insert text)))

Le nom de cette fonction à pour préfixe org-dblock-write: et accepte un seul paramètre d'entrée: params. Le nom du type de bloc est écrit dans le nom de la fonction, après le préfixe. Le paramètre est une plist dans laquelle on retrouve tous les paramètres du bloc. On peut accéder à ces paramètres avec la fonction (plist-get).

Le code de cette fonction est simple: On stock le contenu du paramètre :text dans la variable locale text. Puis on passe le contenu de cette variable à la fonction (insert). Le texte sera automatiquement inséré à l'intérieur de notre bloc dynamique au moment où son contenu sera généré.

On peut maintenant demander à Emacs d'évaluer cette nouvelle fonction.

Elle pourrait être plus simple pour ce qu'on en fait ici. Mais cette exemple, où on utilise une variable locale, est également un bon point de départ si tu souhaite faire un type de bloc plus complexe.

Utilisation du nouveau type de bloc

Maintenant, on peut utiliser le nouveau type de bloc dans un document Org-mode. Mais on doit écrire sois-même les balises #+BEGIN et #+END ainsi que le nom du bloc comme ceci:

#+BEGIN: example :text "Ceci est un test"

#+END

Ici, on précise que le type de bloc est example et on lui donne le paramètre :text avec pour valeur "Ceci est un test".

Maintenant, si on génère le contenu du bloc avec org-dblock-update (raccourcis C-c C-c), voici ce qu'on obtient:

#+BEGIN: example :text "Ceci est un test"
Ceci est un test
#+END

Ça fonctionne. Simple et efficace.

Écriture automatique d'un bloc dans un document Org-mode

On a un nouveau type de bloc, mais si on veut l'utiliser on doit écrire les balises sois-même. La prochaine étape est d'automatiser l'écriture du bloc, par Emacs.

Et pour ça, on va simplement écrire et évaluer une fonction Emacs-lisp interactive. Cette fonction va insérer un exemple de bloc, utilisant notre nouveau type, là où se trouve le curseur d'écriture.

Voici le code de cette fonction:

(defun org-example-insert-dblock ()
  "Create a dynamic block that show an example text"
  (interactive)
  (org-create-dblock
   (list :name "example"
         :text "Un autre example à insérer")))

Elle va appeler une autre fonction: (org-create-dblock). Cette autre fonction accepte comme paramètre une plist, indiquant toutes les propriétés utilisées pour créer le bloc. Ici on précise 2 paramètres: :name est le nom du type de bloc et :text est le paramètre :text du bloc. On indique la valeur de :text comme étant la chaine de caractère "Un autre example à insérer".

Comme cette nouvelle fonction est interactive, on peut l'appeler avec le raccourcis M-x. Si on essaye dans un document Org-mode, voici le résultat qui sera inséré:

#+BEGIN: example :text "Un autre example à insérer"

#+END:

Notre bloc de type example, avec un paramètre :text déjà définit.

À noter que dans notre exemple, la fonction (org-example-insert-dblock) définit la valeur de :text de façon statique. Mais on peut aussi définir n'importe quel paramètre du bloc de façon dynamique.

Maintenant qu'on a une fonction pouvant insérer un bloc de type example, il faut dire à Emacs qu'il peut la proposer quand on appelle org-dynamic-block-insert-dblock.

Pour ça, il suffit d'écrire et d'évaluer le code Emacs-lisp suivant:

(org-dynamic-block-define "example" 'org-example-insert-dblock)

L'appelle à la fonction (org-dynamic-block-define) va associer le nom example avec la fonction org-example-insert-block.

Maintenant si, dans un document Org-mode, on appelle org-dynamic-block-insert-dblock (raccourcis C-c C-x x) et qu'on lui indique le type de bloc example, voici ce qui sera inséré:

#+BEGIN: example :text "Un autre example à insérer"

#+END:

Conclusion

Org-mode propose un excellent moyen de générer dynamiquement des blocs de texte.

L'exemple vu dans ce billet est plutôt simple: On a va seulement insérer un texte, pré-définit par un paramètre. Mais on peut faire bien plus.

Par exemple:

  • Un bloc de type index, qui listerai tous les documents Org-mode d'un dossier
  • Un bloc available-books, qui listerai tout les livres d'une liste de lecture qui seraient disponibles à la bibliothèque ou chez son libraire préféré
  • Un bloc de type machine-list, qui listerai tout les ordinateurs détectés sur le réseau par nmap, ou qui générerai un tableau avec toutes les informations récupérées
  • Un bloc de type next-trains, qui listerai les départs d'un train pour une date, très utile pour préparer ses vacances

Les blocs dynamiques proposent un large éventail de possibilités. Et pour créer de nouveau types, vous avez accès à toutes les bibliothèques disponibles pour Emacs.

Pour en apprendre plus, n'hésite pas à lire la documentation des blocs dynamiques ainsi que celle des fonctions Emacs-lisp utilisée dans de billet de blog.